package com.yunbao.web.servlet;

import com.yunbao.ioc.factory.BeanFactory;
import com.yunbao.ioc.factory.DefaultBeanFactory;
import com.yunbao.ioc.io.Resources;
import com.yunbao.ioc.utils.ClassScanner;
import com.yunbao.web.interceptor.HandlerInterceptor;
import com.yunbao.web.interceptor.HandlerInterceptorRegistry;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * Created by louisyuu on 2020/3/12 下午3:37
 */
public class DispatcherServlet extends HttpServlet {

    private BeanFactory beanFactory;

    private Properties properties = new Properties();


    private List<String> classNames = new ArrayList<>();


    private HandlerMapping handlerMapping;


    //初始化IOC容器以及初始化mvc主件
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {


        //获取上线文配置的文件位置
        String contextConfigLocation = servletConfig.getInitParameter("contextConfigLocation");

        // 1 加载配置文件
        this.properties = Resources.loadPropeteies(contextConfigLocation);

        // 2 扫描相关的类，扫描注解
        classNames = ClassScanner.getClassNames(String.valueOf(properties.get("basePackage")));


        /**
         * 3 初始化bean对象
         *   完成依赖注入
         */
        doRefresh();

        //构造RequestMappingHandlerMapping映射器


        // 5 构造一个HandlerMapping处理器映射器，将配置好的url和Method建立映射关系
        initHandlerMapping();


    }

    private void initHandlerMapping() {
        handlerMapping = new RequestMappingHandlerMapping(this.classNames, beanFactory);
    }

    private void doRefresh() {
        beanFactory = new DefaultBeanFactory(null, String.valueOf(properties.get("basePackage")));
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        HandlerExecutionChain handlerExecutionChain = handlerMapping.getHandler(request);


        HandlerMethod handlerMethod = handlerExecutionChain.getHandlerMethod();
        if (handlerMethod == null) {
            response.setStatus(404);
            response.getWriter().write("404 not found");
            return;
        }
        // 参数绑定
        // 获取所有参数类型数组，这个数组的长度就是我们最后要传入的args数组的长度
        Class<?>[] parameterTypes = handlerMethod.getHandlerMethod().getParameterTypes();

        Object[] args = new Object[parameterTypes.length];

        if (parameterTypes.length > 0) {
            // 以下就是为了向参数数组中塞值，而且还得保证参数的顺序和方法中形参顺序一致
            Map<String, String[]> parameterMap = request.getParameterMap();

            //循环实际的参数，封装到具体的args具体的位置
            for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
                // name=1&name=2  to name [1,2]
                String value = StringUtils.join(param.getValue(), ",");  // 如同 1,2
                Integer paramIndex = handlerMethod.getParameterMapping().get(param.getKey());
                if (paramIndex != null) {
                    args[paramIndex] = value;
                }
            }
            Integer paramIndex = handlerMethod.getParameterMapping().get(HttpServletRequest.class.getSimpleName());
            if (paramIndex != null) {
                args[paramIndex] = request;
            }
            paramIndex = handlerMethod.getParameterMapping().get(HttpServletResponse.class.getSimpleName());
            if (paramIndex != null) {
                args[paramIndex] = response;
            }
        }
        try {
            //前置拦截器
            if (!applyPreHandle(request, response, handlerMethod)) {
                return;
            }

            String result = (String) handlerMethod.getHandlerMethod().invoke(handlerMethod.getHandler(), args);

            //后置处理
            applyPostHandle(request, response, handlerMethod, result);


            response.getWriter().write(result);

        } catch (Exception e) {
            //异常触发完成请求回调
            triggerAfterCompletion(request, response, handlerMethod, e);

            e.printStackTrace();
        }

        triggerAfterCompletion(request, response, handlerMethod, null);


    }


    public boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) {
        Collection<HandlerInterceptor> handlerInterceptors = HandlerInterceptorRegistry.getInstance().getHandlerInterceptors(handlerMethod);

        for (HandlerInterceptor interceptor : handlerInterceptors) {
            try {
                boolean flag = interceptor.preHandle(request, response, handlerMethod);
                if (!flag) {
                    return false;
                }
            } catch (Exception e) {
                //
                triggerAfterCompletion(request, response, handlerMethod, e);
            }
        }
        return true;


    }

    public void applyPostHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Object result) {
        Collection<HandlerInterceptor> handlerInterceptors = HandlerInterceptorRegistry.getInstance().getHandlerInterceptors(handlerMethod);
        for (HandlerInterceptor interceptor : handlerInterceptors) {
            try {
                interceptor.postHandle(request, response, handlerMethod, result);
            } catch (Exception e) {
                //
                triggerAfterCompletion(request, response, handlerMethod, e);
            }
        }
    }


    public void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception e) {
        Collection<HandlerInterceptor> handlerInterceptors = HandlerInterceptorRegistry.getInstance().getHandlerInterceptors(handlerMethod);

        for (HandlerInterceptor interceptor : handlerInterceptors) {
            try {
                interceptor.afterCompletion(request, response, handlerMethod, e);
            } catch (Exception ex) {
                //
            }
        }


    }


}
